package demo.spring.aop.author;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Slf4j
@Component //必须是个bean
public class DemoAspect {


    @Before("execution (* demo.spring.aop.controller..*.*(..))")
    public void auth() {
        log.info("校验权限的我");
    }

    //target会注入目标对象，这里的目标对象就是被切面的具体的控制类
    //args()代表值匹配 没有参数的方法，其他的方法是不会被该表达式匹配的
    @After("execution (* demo.spring.aop.controller..*.*(..)) && target(t) && args()")
    public void closeTest(Object t) {
        log.info("我是最终通知");
        log.info(t.getClass().toString());
    }


    //1、args(s1,s2) 只会匹配有两个String 参数的方法
    //2、s1，s2作为参数会被注入到连接点上，也就是argsTest方法中
    @After("execution (* demo.spring.aop.controller..*.*(..)) && args(s1,s2)")
    public void argsTest(String s1, Integer s2) {
        log.info("测试注入参数");
        log.info(String.format("=====%s,%s======", s1, s2));
    }


    //如果环绕通知 不返回执行结果  就会导致test3()方法不会返回任何结果，导致接口拿不到任何数据
    //所以一定把proceed 返回
    //ProceedingJoinPoint 是 JoinPoint的子类，仅当环绕通知的时候，可以注入ProceedingJoinPoint的连接点
    @Around("execution (* demo.spring.aop.controller.TestController.test3() ) ")
    public Object aroundTest(ProceedingJoinPoint point) {
        try {
            long before = System.currentTimeMillis();
            Object proceed = point.proceed();
            long after = System.currentTimeMillis();
            log.info(String.format("该方法耗时为:%s",after-before));
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return null;
        }
    }

    //JoinPoint 接口提供了一系列有用的方法，
    // 比如 getArgs()（返回方法参数）、
    // getThis()（返回代理对象）、
    // getTarget()（返回目标对象）、
    // getSignature()（返回正在被通知的方法相关信息）和 toString() （打印出正在被通知的方法的有用信息）
    @AfterReturning("execution (* demo.spring.aop.controller.TestController.test4(..) ) ")
    public void JoinPointTest(JoinPoint point){
        System.out.println(Arrays.toString(point.getArgs()));
        System.out.println(point.getThis().getClass());
        System.out.println(point.getTarget().getClass());

    }



    //匹配 spring容器中名字叫testController的bean实体，表示testController实体的所有方法
    //TestController.java被@RestController 注解后，会自动生成一个名为testController的bean。
    //我们也可以显式的声明它的名字 @RestController("testController")来修饰TestController.java
    @AfterThrowing("bean(testController)")
    public void beanPointCut( ){
        log.info("不好意思，异常了");
    }





}
